/**
 * 
 * Provide an SMD to this class as your communication to the client
 * 
 * the SMD must implement the following interface:
 * 
 * fetch(entityClass, args) //args: query, queryOptions{properties}, start, count, sort
 * load(entityClass, id, properties) 
 * save(entityClass, entity) //save or update an entity 
 * deleteItem(entityClass, entity) //delete an entity
 * 
 * This mostly just delegates to your SMD, first tidying a bit so the server doesn't have
 * to know too much, then taking the results and coalescing them to the way they are expected
 * to look for the EntityManager. it also handles all the sync/async mess, so you can always
 * assume you are doing an async call
 * 
 * @author maulin
 */
dojo.provide("medryx.orm.XhrPersistenceService");
dojo.require("medryx.orm.PersistenceService");

dojo.declare("medryx.orm.XhrPersistenceService", medryx.orm.PersistenceService, {
	   xhrService:null,
	   entityManager:null, //REQUIRED -- injected by the EntityManager that you use this service for
	   
	   /**
	    * The easiest way to handle synchronous calls is to change xhr
	    * for a while to force sync. then when this comes back, to make it async
	    * again for the next run. of course this assumes you will be using
	    * xhr for your rpc.
	    */
	   doSynchronous:function(method) {
	   	
	   	   try {
				var originalXhr = dojo.xhr;

				dojo.xhr = function(method, args, hasBody){
					args.sync = true;
					originalXhr.apply(dojo, arguments);
				}
				
				return method();
				
			} finally {
				dojo.xhr = originalXhr;
			}
	   },
	   
	   constructor:function(xhrService) {
	       this.xhrService = xhrService;
		   this.sync = false;
	   },
	
	    /**
         * This maps to the ReadApi's fetch method
         * 
         * the deferred calls back with an object:
         * {
         *    total: total number of records available for this query, or -1 if unknown
         *    items:array of items
         * }
         * returns a deferred (always)
         * 
         * @param {Object} kwArgs
         */
        fetch:function(entityClass, args, sync) {
			var method = dojo.hitch(this, "doFetch", entityClass, args);
			if (sync) {
				return this.doSynchronous(method);
			} else {
				return method();
			}
		},
		
		doFetch:function(entityClass, args) {
			var deferred = this.xhrService.fetch(entityClass, args);
            deferred.addCallback(function(rpcObject) {
                return rpcObject.result || rpcObject;
            });
            return deferred;
		},
        
        /**
         * returns a deferred (always async)
         * 
         * @param {Object} entity
         */
        save:function(className, id, propertiesToPersist) {
			var deferred = this.xhrService.save(className, id, propertiesToPersist);
			deferred.addCallback(function(rpcObject) {
                return rpcObject.result || rpcObject;
            });
			return deferred;
		},
        
        /**
         * maps to WriteApi deleteItem
         * 
         * returns a deferred (always async)
         * 
         * @param {Object} entity
         */
        deleteItem:function(className, id) {
			var deferred = this.xhrService.deleteItem(className, id);
			deferred.addCallback(function(rpcObject) {
                return rpcObject.result || rpcObject;
            });
			return deferred;
		},
        
        /**
         * given an entiyClass, id, and properties, full load an instance 
         * that guarantees that each property in properties is initialized
         * (or if no properties are specified, then some contract
         * between client and server about "default" loading has
         * been fulfilled)
         */
        load:function(entityClass, id, propertiesToLoad, sync) {
			var method = dojo.hitch(this, "doLoad", entityClass, id, propertiesToLoad);
			if (sync) {
				return this.doSynchronous(method);
			} else {
			     return method();
			}
			
		},
		
		doLoad:function(entityClass, id, propertiesToLoad) {
			var deferred = this.xhrService.load(this.entityManager.getClassName(entityClass), id, propertiesToLoad);
            deferred.addCallback(function(rpcObject) {
                return rpcObject.result || rpcObject;
            });
            return deferred;
		}
});
